home *** CD-ROM | disk | FTP | other *** search
Text File | 1996-04-25 | 10.1 KB | 255 lines | [TEXT/MPS ] |
- //========================================================================================
- //
- // File: FWExcImp.h
- // Release Version: $ ODF 1 $
- //
- // Copyright: (c) 1993 - 1996 by Apple Computer, Inc., all rights reserved.
- //
- //========================================================================================
-
- //========================================================================================
- // THEORY OF OPERATION
- //
- // This subsystem provides an emulation for C++ exception handling for compilers that
- // do not yet support exceptions.
- //
- // When an exception is thrown, the stack is unwound to the first enclosing try/catch
- // block. Automatic (stack) objects are destroyed during stack unwinding. Exceptions
- // thrown during the construction of either an automatic or a dynamic (heap) object will
- // result in the fully constructed subobjects of the partially constructed object
- // being destroyed.
- //
- // To obtain this behavior with exception handling emulation, the programmer must do
- // extra work:
- //
- // 1) Classes which are to be destroyed during unwinding must be "registered"
- // as auto-destruct classes, using the FW_DECLARE_AUTO and FW_DEFINE_AUTO
- // macros
- //
- // 2) The destructor and every constructor of auto-destruct classes must invoke
- // the FW_START_DESTRUCTOR and FW_END_CONSTRUCTOR macros (respectively).
- //
- // 3) Auto-destruct objects allocated on the heap must be allocated using the
- // FW_NEW macro instead of using a plain new expression.
- //
- // For the emulation to work, the code executed by the FW_END_CONSTRUCTOR and
- // FW_START_DESTRUCTOR macros must determine the storage class of the object being
- // constructed/destructed. There are three possibilities: 1) automatic, 2) static, and
- // 3) dynamic.
-
- // Automatic objects must be tracked by the exception emulation.
- // Determining if the address of an object is on the stack is platform dependent but
- // generally easy to do. We currently assume that it is possible to obtain a value
- // for the stack base when the exception system in initialized, and store this value
- // in the exception globals. Care must be taken in a threaded environment with
- // multiple stacks. The stack top is determined by simply calling a function that
- // returns the address of a local variable.
- //
- // Static objects need not be tracked by the exception emulation, so
- // the FW_END_CONSTRUCTOR and FW_START_DESTRUCTOR macros should act as no-ops.
- // Unfortunately, there is no easy/portable way to determine if an address is located
- // in the static data area. We attempt to determine if the object is in static data
- // via the process of elimination. If the object is neither automatic or dynamic, then
- // it is presumed to be static. Unfortunately, this process of elimination gives incorrect
- // results when programmer makes a particular mistake. The mistake is relatively easy to
- // make, and cannot be detected at compile time. The mistake is made when a programmer
- // creates a class that is not declared to be auto-destruct, but embeds an auto-destruct
- // member object (by value) in the class. When such a class is allocated in the heap
- // via a new expression.
- //
- // Dynamic auto-destruct objects must be allocated via the FW_NEW macro. The FW_NEW
- // macro invokes a special overloaded variant of operator new, and this variant registers
- // the address range of the object being dynamically constructed. This makes it
- // relatively easy to determine if an address is that of an object (or subobject)
- // currently being dynamically constructed.
- //
- // There are several weaknesses with this mechanism for determining the storage class
- // of an object.
- //
- // First, if a class has member objects that are autodestruct objects,
- // but the class itself is not autodestruct, then if it is allocated dynamically the
- // member objects will not appear to be dynamic objects. They are correctly rejected
- // as automatic objects, so they are incorrectly classified as static objects, and the
- // emulation incorrectly decides they need not be tracked. The debug version of the
- // emulation detects this problem and issues a warning. Unfortunately, the warning
- // might be issued for bonafide static objects, described next.
- //
- // Second, any object which is not within a try-block context is assumed to be static.
- // This assumption will always be valid in an application that does not dynamically
- // load shared libraries at runtime. (Not true! What about compilers/runtimes that
- // delay construction of static objects until the first function call in a translation
- // unit?)
- //
- //========================================================================================
-
- #ifndef FWEXCIMP_H
- #define FWEXCIMP_H
-
- #ifndef FWENVDEF_H
- #include "FWEnvDef.h"
- #endif
-
- #ifndef SLPRIMEM_H
- #include "SLPriMem.h"
- #endif
-
- #ifndef FWCLAIMP_H
- #include "FWClaImp.h"
- #endif
-
- #include <setjmp.h>
-
- #if defined(FW_BUILD_MAC) && !defined(__TYPES__)
- #include <Types.h>
- #endif
-
- //========================================================================================
- // Forward Declarations
- //========================================================================================
-
- FW_EXTERN_C_BEGIN
-
- struct FW_SPrivTryBlockContext;
- struct FW_SPrivWatcher;
- struct FW_SPrivDeleteElem;
- struct FW_SPrivExceptionInfo;
-
- typedef void (*FW_PrivDestroyProc)(void* self);
- typedef void (*FW_PrivDeleteProc)(void* self);
- typedef void (*FW_PrivCloneProc)(void* source, void* destination, size_t destSize);
- typedef void (*FW_PrivLongJumpProc)(jmp_buf,int);
-
- #ifdef FW_DEBUG
- void FW_AutoConstructed(void* object, size_t size, FW_PrivDestroyProc destroyer, char* name);
- void FW_AutoDestructed(void* object, FW_PrivDestroyProc destroyer, char* name);
- #else
- void FW_AutoConstructed(void* object, size_t size, FW_PrivDestroyProc destroyer);
- void FW_AutoDestructed(void* object);
- #endif
-
- void FW_PrivCaughtException(void* exception, size_t size, FW_SClassInfoPtr itsClass, FW_PrivDestroyProc itsDestroyer, FW_PrivCloneProc itsCloner);
- void FW_PrivCaughtReferenceException();
- void FW_PrivCaughtNoInstanceException();
- void FW_PrivCaughtEverythingException();
- void FW_PrivKeepThrowing();
- void FW_PrivCatchCleanup();
- void FW_PrivThrow(void* exception, size_t size, FW_SClassInfoPtr itsClass, FW_PrivDestroyProc itsDestroyer, FW_PrivCloneProc itsCloner);
- void FW_PrivThrowSame();
-
- FW_Boolean FW_PrivCanCatchThisException(FW_SClassInfoPtr targetClass);
-
- void FW_PrivTryBlockContext_Init(FW_SPrivTryBlockContext *self, jmp_buf buffer, FW_PrivLongJumpProc jumpProc);
- void FW_PrivTryBlockContext_Destroy(void *self);
- FW_SPrivTryBlockContext* FW_PrivTryBlockContext_MakeCurrent(FW_SPrivTryBlockContext* context);
-
- void FW_PrivWatcher_Init(FW_SPrivWatcher* self, FW_PrivDeleteProc deleter);
- void* FW_PrivWatcher_Pop();
- void FW_PrivWatcher_Destroy(void* self);
- void* FW_PrivWatcher_New(void*p, size_t size, FW_SPrivWatcher* self);
-
- void* FW_PrivGetThrownException();
-
- //========================================================================================
- // volatile kludge
- //========================================================================================
-
- // The FW_VOLATILE macro is used to workaround a problem that can happen with
- // setjmp/longjmp when variables are placed in registers. The macro takes the address
- // of the variable in an attempt to convince the compiler to not place the variable in
- // a register. However, the result is not used, the compiler is free to eliminate the
- // expression, and then notice that the variable can be placed in register after all.
- // So, we take the address and place the result in this global.
-
- extern void* FW_gPrivVolatileKludge;
-
- //========================================================================================
- // struct FW_SPrivExceptionInfo
- //========================================================================================
-
- struct FW_SPrivExceptionInfo
- {
- void* fAddress;
- size_t fSize;
- FW_SClassInfoPtr fClass;
- FW_PrivDestroyProc fDestroyer;
- FW_PrivCloneProc fCloner;
- };
-
- //========================================================================================
- // struct FW_SPrivDeleteElem
- //========================================================================================
-
- struct FW_SPrivDeleteElem
- {
- void* fObject;
- FW_PrivDestroyProc fDestroyer;
- #ifdef FW_DEBUG
- char* fName;
- FW_Boolean fHeapAllocated;
- #endif
- };
-
- //========================================================================================
- // struct FW_SPrivWatcher
- //========================================================================================
-
- struct FW_SPrivWatcher
- {
- FW_SPrivWatcher* fNext;
- void* fObject;
- void* fLimit;
- FW_PrivDeleteProc fDeleter;
- #ifdef FW_DEBUG
- long fDeleteElems;
- #endif
- };
-
- //========================================================================================
- // struct FW_SPrivTryBlockContext
- //========================================================================================
-
- struct FW_SPrivTryBlockContext
- {
- FW_SPrivTryBlockContext* fPriorContext;
- long fDeleteStackLevel;
- jmp_buf* fJumpBuffer;
- FW_PrivLongJumpProc fJumpProc;
- void* fPriorStackBase;
- void* fPriorProcessBase;
- };
-
- //========================================================================================
- // Design Notes
- //========================================================================================
-
- /*
-
- At construction time:
- If stack or current heap object, Push DeleteElem on stack
-
- At end of FW_NEW
- Walk DeleteStack, removing entries for this object
-
- At destruction time:
- if the object is a stack based object, remove entry from DeleteStack
-
- At stack-unwind time
- Delete each elem on delete stack
-
- At end of try-block scope
- Verify delete stack is back to initial state
-
- Note: the above approach assumes a try-way distinction for storage class of objects.
- Objects may be curent-heap, stack, or other. Current-heap objects are objects currently
- being allocated via FW_NEW. There is effectively a stack of such objects, in case
- the constructor of an object being allocated with FW_NEW calls FW_NEW to allocate
- another object. The current-heap object is the top element of this stack. The other
- category thus includes static objects and heap based objects other than the current-heap
- object.
-
- */
-
- FW_EXTERN_C_END
-
- #endif
-